﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Silverlight3dApp.Learning.Lesson08
{
    public class ModelManager : DrawableGameComponent
    {
        private Random _random = new Random();

        private readonly List<BasicModel> _models = new List<BasicModel>();
        private readonly List<BasicModel> _shots = new List<BasicModel>();
        private float _shotMinZ = -1000;

        private Vector3 _maxSpawnLocation = new Vector3(100, 100, -3000);
        private int _nextSpawnTime;
        private int _timeSinceLastSpawn;
        private float _maxRollAnglePerFrame = MathHelper.Pi / 30;

        private int _enemiesThisLevel;
        private int _missedThisLevel;

        private int _currentLevel;

        private readonly List<LevelInfo> _levelInfos = new List<LevelInfo>();


        public ModelManager(Game game) : base(game)
        {
            _levelInfos.Add(new LevelInfo
                                {
                                    MinSpawnTime = 1000,
                                    MaxSpawnTime = 3000,
                                    NumberEnemies = 20,
                                    MinSpeed = 2,
                                    MaxSpeed = 6,
                                    MissesAllowd = 10
                                });
            _levelInfos.Add(new LevelInfo
                                {
                                    MinSpawnTime = 900,
                                    MaxSpawnTime = 2800,
                                    NumberEnemies = 22,
                                    MinSpeed = 2,
                                    MaxSpeed = 6,
                                    MissesAllowd = 9
                                });
            _levelInfos.Add(new LevelInfo
                                {
                                    MinSpawnTime = 800,
                                    MaxSpawnTime = 2600,
                                    NumberEnemies = 24,
                                    MinSpeed = 2,
                                    MaxSpeed = 6,
                                    MissesAllowd = 8
                                });
            _levelInfos.Add(new LevelInfo
                                {
                                    MinSpawnTime = 700,
                                    MaxSpawnTime = 2400,
                                    NumberEnemies = 26,
                                    MinSpeed = 3,
                                    MaxSpeed = 7,
                                    MissesAllowd = 7
                                });
            _levelInfos.Add(new LevelInfo
                                {
                                    MinSpawnTime = 600,
                                    MaxSpawnTime = 2200,
                                    NumberEnemies = 28,
                                    MinSpeed = 3,
                                    MaxSpeed = 7,
                                    MissesAllowd = 6
                                });
            _levelInfos.Add(new LevelInfo
                                {
                                    MinSpawnTime = 500,
                                    MaxSpawnTime = 2000,
                                    NumberEnemies = 30,
                                    MinSpeed = 3,
                                    MaxSpeed = 7,
                                    MissesAllowd = 5
                                });
            _levelInfos.Add(new LevelInfo
                                {
                                    MinSpawnTime = 400,
                                    MaxSpawnTime = 1800,
                                    NumberEnemies = 32,
                                    MinSpeed = 4,
                                    MaxSpeed = 7,
                                    MissesAllowd = 4
                                });
            _levelInfos.Add(new LevelInfo
                                {
                                    MinSpawnTime = 300,
                                    MaxSpawnTime = 1600,
                                    NumberEnemies = 34,
                                    MinSpeed = 4,
                                    MaxSpeed = 8,
                                    MissesAllowd = 3
                                });
            _levelInfos.Add(new LevelInfo
                                {
                                    MinSpawnTime = 200,
                                    MaxSpawnTime = 1400,
                                    NumberEnemies = 36,
                                    MinSpeed = 5,
                                    MaxSpeed = 8,
                                    MissesAllowd = 2
                                });
            _levelInfos.Add(new LevelInfo
                                {
                                    MinSpawnTime = 100,
                                    MaxSpawnTime = 1200,
                                    NumberEnemies = 38,
                                    MinSpeed = 5,
                                    MaxSpeed = 9,
                                    MissesAllowd = 1
                                });
        }

        private void SetNextSpawnTime()
        {
            _nextSpawnTime = _random.Next(_levelInfos[_currentLevel].MinSpawnTime, _levelInfos[_currentLevel].MaxSpawnTime);
            _timeSinceLastSpawn = 0;
        }

        public void SpawnEnemy()
        {
            var position = new Vector3(_random.Next((int) (-_maxSpawnLocation.X), (int) _maxSpawnLocation.X), _random.Next((int) (-_maxSpawnLocation.Y), (int) _maxSpawnLocation.Y), _maxSpawnLocation.Z);
            var direction = new Vector3(0, 0, _random.Next(_levelInfos[_currentLevel].MinSpeed, _levelInfos[_currentLevel].MaxSpeed));
            var roll = (float) (_random.NextDouble() * _maxRollAnglePerFrame - (_maxRollAnglePerFrame / 2));
            _models.Add(new SpinningEnemy(Game.Content.Load<Model>(@"Models/spaceship"), position, direction, 0, 0, roll));
            _enemiesThisLevel++;
            SetNextSpawnTime();
        }

        public void AddShot(Vector3 position, Vector3 direction)
        {
            _shots.Add(new SpinningEnemy(Game.Content.Load<Model>(@"Models/ammo"), position, direction, 0, 0, 0));
        }

        public override void Initialize()
        {
            SetNextSpawnTime();
            base.Initialize();
        }

        protected void CheckToSpawnEnemy(GameTime gameTime)
        {
            if (_enemiesThisLevel < _levelInfos[_currentLevel].NumberEnemies)
            {
                _timeSinceLastSpawn += gameTime.ElapsedGameTime.Milliseconds;
                if (_timeSinceLastSpawn > _nextSpawnTime)
                {
                    SpawnEnemy();
                }
            }
        }

        protected void UpdateModels()
        {
            foreach (var model in _models.ToArray())
            {
                model.Update();
                if (model.World.Translation.Z > ((MadnessGame) Game).Camera.CameraPosition.Z + 100)
                {
                    _models.Remove(model);
                }
            }
        }

        protected void UpdateShots()
        {
            foreach (var shot in _shots.ToArray())
            {
                shot.Update();
                if (shot.World.Translation.Z < _shotMinZ)
                {
                    _shots.Remove(shot);
                }
                else
                {
                    BasicModel shotCopy = shot;
                    foreach (var model in _models.Where(model => shotCopy.CollideWith(model.Model, model.World)).ToArray())
                    {
                        _models.Remove(model);
                        _shots.Remove(shot);
                        break;
                    }
                }
            }
        }

        public override void Update(GameTime gameTime)
        {
            CheckToSpawnEnemy(gameTime);
            UpdateModels();
            UpdateShots();
            base.Update(gameTime);
        }

        public override void Draw(GameTime gameTime)
        {
            foreach (var model in _models)
            {
                model.Draw(((MadnessGame) Game).Camera);
            }
            foreach (var shot in _shots)
            {
                shot.Draw(((MadnessGame)Game).Camera);
            }
            base.Draw(gameTime);
        }
    }
}